#pragma once

enum TypeConv
{
	TypeConv_None, // Conversion not possible.
	TypeConv_Explicit, // Only explicit conversion possible.
	TypeConv_Implicit_Warning, // Explicit and implicit conversion possible, but generates complier warning when implicit.
	TypeConv_Implicit, // Explicit and implicit conversion possible.
};

struct TypeConvRules
{
	enum Rule
	{
		Rule_Any_to_Bool,
		Rule_Bool_to_Numeric,
		Rule_Any_to_String,
		Rule_String_to_Any,
		Rule_Unsigned_vs_Signed,
		Rule_Bigger_to_Smaller,
		Rule_Real_vs_Integer,
		Rule_Char_vs_Numeric,
		RuleCount
	};

	TypeConv RuleArr[RuleCount];

	// Initializes with default rules.
	TypeConvRules();
};

class Type
{
public:
	enum TYPE
	{
		TYPE_AUTO, // Changed to some real type during preprocessing.
		TYPE_VOID,
		TYPE_UINT8,
		TYPE_UINT16,
		TYPE_UINT32,
		TYPE_UINT64,
		TYPE_INT8,
		TYPE_INT16,
		TYPE_INT32,
		TYPE_INT64,
		TYPE_FLOAT,
		TYPE_DOUBLE,
		TYPE_BOOL,
		TYPE_CHAR,
		TYPE_STRING,
		TYPE_COUNT
	};
	static const char * TYPE_NAMES[];

	enum CLASS
	{
		CLASS_NOT_EMPTY = 0x01, // Any type except TYPE_COUNT.
		CLASS_COMPILED  = 0x02, // Any NOT_EMPTY except auto.
		CLASS_PHYSICAL  = 0x04, // Any COMPLILED except void. Has GetSize() > 0.
		CLASS_NUMERIC   = 0x08, // Any PHYSICAL that is numeric.
		CLASS_INTEGER   = 0x10, // Any NUMERIC that is integer.
		CLASS_REAL      = 0x20, // Any NUMERIC that is floating-point.
		CLASS_UNSIGNED  = 0x40, // Any INTEGER that cannot be negative.
		CLASS_SIGNED    = 0x80, // Any INTEGER that can be negative.
	};
	static const uint TYPE_CLASSES[];

	enum CONV_TYPE
	{
		CONV_NONE,
		CONV_EXPLICIT,
		CONV_IMPLICIT,
	};

	Type() : m_Type(TYPE_COUNT) { }
	Type(TYPE type) : m_Type(type) { }

	bool operator == (const Type &rhs) const;
	bool operator != (const Type &rhs) const { return ! this->operator==(rhs); }

	void Clear() { m_Type = TYPE_COUNT; }
	void Set(TYPE type) { m_Type = type; }

	TYPE GetType() const { return m_Type; }
	uint GetClasses() const { return TYPE_CLASSES[m_Type]; }
	bool CheckClass(CLASS cls) const { return (TYPE_CLASSES[m_Type] & cls) != 0; }
	bool IsEmpty() const { return !CheckClass(CLASS_NOT_EMPTY); }

	bool IsRaw() const { return TypeIsRaw(m_Type); }
	bool IsEquatable() const;
	// If type is not real and has no size, returns 0.
	uint GetSize() const;
	TypeConv CheckConvFrom(const Type &srcType, const TypeConvRules &rules) const;
	
	void GetName(string &out) const;
	string GetName_r() const { string result; GetName(result); return result; }

	void Promote(const Type &lhs, const Type &rhs);

	////// Dealing with values
	void AllocValue(void *addr) const;
	void FreeValue(void *addr) const;
	void CopyValue(void *dstAddr, const void *srcAddr) const;
	void InitValue(void *addr) const;
	bool ValuesEqual(const void *addr1, const void *addr2) const;

private:
	static bool TypeIsRaw(TYPE type);

	TYPE m_Type;
};

struct TypeSuggestion
{
	Type T; // Can be empty.
	uint Class; // Can be 0.

	// Empty suggestion
	TypeSuggestion() : Class(0) { }
	// Initialized suggestion
	TypeSuggestion(const Type &t, uint class_) : T(t), Class(class_) { }

	void Clear() { T.Clear(); Class = 0; }
	bool IsEmpty() const { return T.IsEmpty() && Class == 0; }
};

void TypeVectorToStr(string &out, const std::vector<Type> &types);
